home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 2: CDPD 1 / Almathera Ten on Ten - Disc 2: CDPD 1.iso / pd / 176-200 / 189 / nethack / eenamiga.zoo / Amiga / amigaDos.c next >
C/C++ Source or Header  |  1988-07-29  |  17KB  |  770 lines

  1. /*    SCCS Id: @(#)amigaDos.c msdos.c - Amiga version   2.3    88/07/24
  2. /* An assortment of MSDOS functions.
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include "hack.h"
  7.  
  8. #undef TRUE
  9. #undef FALSE
  10. #undef COUNT
  11.  
  12. #include <libraries/dos.h>
  13.  
  14. extern char Initialized;
  15.  
  16. struct FileLock *Lock(), *CurrentDir(); /* Cheating - BCPL pointers */
  17. struct FileHandle *Open();              /* Cheating - BCPL pointer */
  18. long Read(), Write(), IoErr(), AvailMem();
  19. void *malloc();
  20. char *rindex(), *index();
  21.  
  22. int Enable_Abort = 0;    /* for stdio package */
  23.  
  24. /* Initial path, so we can find NetHack.cnf */
  25.  
  26. char PATH[PATHLEN] = "Ram:;df0:;NetHack:";
  27.  
  28. void
  29. flushout()
  30. {
  31.     (void) fflush(stdout);
  32. }
  33.  
  34. getuid() {
  35.     return 1;
  36. }
  37.  
  38. /*
  39.  *  Actually make up a process id.
  40.  *  Makes sure one can mess less with saved levels...
  41.  */
  42.  
  43. int getpid()
  44. {
  45.     static short pid;
  46.  
  47.     while (pid == 0) {
  48.     struct DateStamp dateStamp;
  49.     pid = rnd(30000);
  50.     pid += DateStamp(&dateStamp);    /* More or less random */
  51.     pid ^= (short) (dateStamp.ds_Days >> 16) ^
  52.            (short) (dateStamp.ds_Days)       ^
  53.            (short) (dateStamp.ds_Minute)     +
  54.            (short) (dateStamp.ds_Tick);
  55.     pid %= 30000;
  56.     }
  57.  
  58.     return pid;
  59. }
  60.  
  61. char *
  62. getlogin() {
  63.     return ((char *) NULL);
  64. }
  65.  
  66. int
  67. abs(x)
  68. int x;
  69. {
  70.     return x < 0? -x: x;
  71. }
  72.  
  73. #ifdef REDO
  74. tgetch() {
  75.     char ch, popch();
  76.  
  77.     if (!(ch = popch())) {
  78.     ch = WindowGetchar();
  79.     }
  80.     return ((ch == '\r') ? '\n' : ch);
  81. }
  82. #else /* REDO /**/
  83. tgetch() {
  84.     char ch;
  85.  
  86.     ch = WindowGetchar();
  87.     return ((ch == '\r') ? '\n' : ch);
  88. }
  89. #endif /* REDO /**/
  90.  
  91.  
  92. #ifdef DGK
  93. # include <ctype.h>
  94. # include <fcntl.h>
  95.  
  96. # define Sprintf (void) sprintf
  97.  
  98. # ifdef SHELL
  99. dosh()
  100. {
  101.     pline("No mysterious force prevented you from using multitasking.");
  102.     return 0;
  103. }
  104. # endif /* SHELL */
  105.  
  106.  
  107. /* construct the string  file.level */
  108. void
  109. name_file(file, level)
  110. char *file;
  111. int level;
  112. {
  113.     char *tf;
  114.  
  115.     if (tf = rindex(file, '.'))
  116.     Sprintf(tf+1, "%d", level);
  117. }
  118.  
  119. #define ID_DOS1_DISK    'DOS\1'
  120.  
  121. /*
  122.  *  This routine uses an approximation of the free bytes on a disk.
  123.  *  How large a file you can actually write depends on the number of
  124.  *  extension blocks you need for it.
  125.  *  An iterative approach is used that (hopefully) mimics the AmigaDOG
  126.  *  (yuck, tripos) block allocation sequence used when a file grows.
  127.  */
  128. long
  129. freediskspace(path)
  130. char *path;
  131. {
  132.     register long freeBytes = 0;
  133.     register struct InfoData *infoData; /* Remember... longword aligned */
  134.     char fileName[32];
  135.  
  136.     /*
  137.      *    Find a valid path on the device of which we want the free space.
  138.      *    If there is a colon in the name, it is an absolute path
  139.      *    and all up to the colon is everything we need.
  140.      *    Remember slashes in a volume name are allowed!
  141.      *    If there is no colon, it is relative to the current directory,
  142.      *    so must be on the current device, so "" is enough...
  143.      */
  144.     {
  145.     register char *colon;
  146.  
  147.     strncpy(fileName, path, sizeof(fileName)-1);
  148.     fileName[31] = 0;
  149.     if (colon = index(fileName, ':'))
  150.         colon[1] = '\0';
  151.     else
  152.         fileName[0] = '\0';
  153.     }
  154.  
  155.     if (infoData = malloc(sizeof(*infoData))) {
  156.     struct FileLock *fileLock;  /* Cheating */
  157.     if (fileLock = Lock(fileName, SHARED_LOCK)) {
  158.         if (Info(fileLock, infoData)) {
  159.         /* We got a kind of DOS volume, since we can Lock it. */
  160.         /* Calculate number of blocks available for new file */
  161.         /* Kludge for the ever-full VOID: (oops RAM:) device */
  162.         if (infoData->id_UnitNumber == -1 &&
  163.             infoData->id_NumBlocks == infoData->id_NumBlocksUsed) {
  164.             freeBytes = AvailMem(0L) - 48*1024;
  165.             /* Just a stupid guess at the */
  166.             /* Ram-Handler overhead per block: */
  167.             freeBytes -= freeBytes/16;
  168.         } else {
  169.             /* Normal kind of DOS file system device/volume */
  170.             register long fileBlocks;
  171.             register long extensionPointers;
  172.             freeBytes = infoData->id_NumBlocks -
  173.                 infoData->id_NumBlocksUsed -
  174.                 1;  /* for file header */
  175.             if (infoData->id_DiskType == ID_DOS_DISK)
  176.             /* BytesPerBlock is 488 on floppies */
  177.             extensionPointers = (infoData->id_BytesPerBlock-200)/4;
  178.             else
  179.             /* Presumably it is 512 on ID_DOS1_DISK. */
  180.             extensionPointers = (infoData->id_BytesPerBlock-224)/4;
  181.             /* Number of blocks left to simulate */
  182.             fileBlocks = freeBytes;
  183.             /* Need another extension block? */
  184.             while (fileBlocks > extensionPointers) {
  185.             /* Get an extension block. One block less free. */
  186.             freeBytes--;
  187.             fileBlocks--;
  188.             /* to accomodate 72 extra data blocks */
  189.             fileBlocks -= extensionPointers;
  190.             }
  191.             freeBytes *= infoData->id_BytesPerBlock;
  192.         }
  193.         if (freeBytes < 0)
  194.             freeBytes = 0;
  195.         }
  196.         UnLock(fileLock);
  197.     }
  198.     free(infoData);
  199.     }
  200.     return freeBytes;
  201. }
  202.  
  203.  
  204. long
  205. filesize(file)
  206. char *file;
  207. {
  208.     register struct FileLock *fileLock;
  209.     register struct FileInfoBlock *fileInfoBlock;
  210.     register long size = 0;
  211.  
  212.     if (fileInfoBlock = malloc(sizeof(*fileInfoBlock))) {
  213.     if (fileLock = Lock(file, SHARED_LOCK)) {
  214.         if (Examine(fileLock, fileInfoBlock)) {
  215.         size = fileInfoBlock->fib_Size;
  216.         }
  217.         UnLock(fileLock);
  218.     }
  219.     free(fileInfoBlock);
  220.     }
  221.     return size;
  222. }
  223.  
  224. /*
  225.  *  On the Amiga, looking if a specific file exists is much faster
  226.  *  than sequentially reading a directory.
  227.  */
  228.  
  229. void
  230. eraseall(path, files)
  231. char *path, *files;
  232. {
  233.     char buf[FILENAME];
  234.     short i;
  235.     struct FileLock *fileLock, *dirLock;
  236.  
  237.     if (dirLock = Lock(path)) {
  238.     dirLock = CurrentDir(dirLock);
  239.  
  240.     strcpy(buf, files);
  241.     for (i = 0; i < MAXLEVEL; i++) {
  242.         name_file(buf, i);
  243.         if (fileLock = Lock(buf, SHARED_LOCK)) {
  244.         UnLock(fileLock);
  245.         DeleteFile(buf);
  246.         }
  247.     }
  248.  
  249.     UnLock(CurrentDir(dirLock));
  250.     }
  251. }
  252.  
  253. /* This size makes that most files can be copied with two Read()/Write()s */
  254.  
  255. #define COPYSIZE    4096
  256.  
  257. char *CopyFile(from, to)
  258. char *from, *to;
  259. {
  260.     register struct FileHandle *fromFile, *toFile;
  261.     register char *buffer;
  262.     register long size;
  263.     char *error = NULL;
  264.  
  265.     if (buffer = malloc(COPYSIZE)) {
  266.     if (fromFile = Open(from, MODE_OLDFILE)) {
  267.         if (toFile = Open(to, MODE_NEWFILE)) {
  268.         while (size = Read(fromFile, buffer, (long)COPYSIZE)) {
  269.             if (size != Write(toFile, buffer, size)) {
  270.             error = "Write error";
  271.             break;
  272.             }
  273.         }
  274.         Close(toFile);
  275.         } else /* Can't open destination file */
  276.         error = "Cannot open destination";
  277.         Close(fromFile);
  278.     } else /* Cannot open source file. Should not happen. */
  279.         error = "Huh?? Cannot open source??";
  280.     free(buffer);
  281.     return error;
  282.     } else /* Cannot obtain buffer for copying */
  283.     return "No Memory !";
  284. }
  285.  
  286. void
  287. copybones(mode)
  288. int mode;
  289. {
  290.     struct FileLock *fileLock;
  291.     char from[FILENAME], to[FILENAME];
  292.     char *frompath, *topath, *status;
  293.     short i;
  294.     extern int saveprompt;
  295.  
  296.     if (!ramdisk)
  297.     return;
  298.  
  299.     frompath = (mode != TOPERM) ? permbones : levels;
  300.     topath = (mode == TOPERM) ? permbones : levels;
  301.  
  302.     /* Remove any bones files in `to' directory. */
  303.     eraseall(topath, allbones);
  304.  
  305.     /* Copy `from' to `to' */
  306.     strcpy(from, frompath);
  307.     strcat(from, allbones);
  308.     strcpy(to, topath);
  309.     strcat(to, allbones);
  310.  
  311.     for (i = 1; i < MAXLEVEL; i++) {
  312.     name_file(from, i);
  313.     name_file(to, i);
  314.     if (fileLock = Lock(from, SHARED_LOCK)) {
  315.         UnLock(fileLock);
  316.         if (status = CopyFile(from, to))
  317.         goto failed;
  318.     }
  319.     }
  320.  
  321.     /*
  322.      * The last file got there.  Remove the ramdisk bones files.
  323.      */
  324.     if (mode == TOPERM)
  325.     eraseall(frompath, allbones);
  326.     return;
  327.  
  328.     /* Last file didn't get there. */
  329.  
  330. failed:
  331.     msmsg("Cannot copy `%s' to `%s'\n(%s)\n", from, to, status);
  332.  
  333.     if (mode == TOPERM) {
  334.     msmsg("Bones will be left in `%s'\n",
  335.         *frompath ? frompath : hackdir);
  336.     return;
  337.     } else {
  338.     /* Remove all bones files on the RAMdisk */
  339.     eraseall(levels, allbones);
  340.     playwoRAMdisk();
  341.     }
  342. }
  343.  
  344. playwoRAMdisk()
  345. {
  346.     msmsg("Do you wish to play without a RAMdisk (y/n) ? ");
  347.  
  348.     /* Set ramdisk false *before* exit'ing (because msexit calls
  349.      * copybones)
  350.      */
  351.     ramdisk = FALSE;
  352.     if (getchar() != 'y') {
  353.     settty("Be seeing you ...\n");
  354.     exit(0);
  355.     }
  356.     set_lock_and_bones();
  357.     return;
  358. }
  359.  
  360. saveDiskPrompt(start)
  361. {
  362.     extern int saveprompt;
  363.     char buf[BUFSIZ], *bp;
  364.     struct FileLock *fileLock;
  365.  
  366.     if (saveprompt) {
  367.     /* Don't prompt if you can find the save file */
  368.     if (fileLock = Lock(SAVEF, SHARED_LOCK)) {
  369.         UnLock(fileLock);
  370.         return 1;
  371.     }
  372.     remember_topl();
  373.     home();
  374.     cl_end();
  375.     msmsg("If save file is on a SAVE disk, put that disk in now.\n");
  376.     cl_end();
  377.     msmsg("File name (default `%s'%s) ? ", SAVEF,
  378.         start ? "" : ", <Esc> cancels save");
  379.     getlin(buf);
  380.     home();
  381.     cl_end();
  382.     curs(1, 2);
  383.     cl_end();
  384.     if (!start && *buf == '\033')
  385.         return 0;
  386.  
  387.     /* Strip any whitespace. Also, if nothing was entered except
  388.      * whitespace, do not change the value of SAVEF.
  389.      */
  390.     for (bp = buf; *bp; bp++)
  391.         if (!isspace(*bp)) {
  392.         strncpy(SAVEF, bp, PATHLEN);
  393.         break;
  394.         }
  395.     }
  396.     return 1;
  397. }
  398.  
  399. /* Return 1 if the record file was found */
  400. static boolean
  401. record_exists()
  402. {
  403.     FILE *file;
  404.  
  405.     if (file = fopenp(RECORD, "r")) {
  406.     fclose(file);
  407.     return TRUE;
  408.     }
  409.     return FALSE;
  410. }
  411.  
  412. /* Prompt for game disk, then check for record file.
  413.  */
  414. void
  415. gameDiskPrompt()
  416. {
  417.     extern int saveprompt;
  418.  
  419.     if (record_exists())
  420.     return;
  421.  
  422.     if (saveprompt) {
  423.     (void) putchar('\n');
  424.     getreturn("when the GAME disk has been put in");
  425.     }
  426.  
  427.     if (!record_exists()) {
  428.     msmsg("\n\nWARNING: can't find record file `%s'!\n", RECORD);
  429.  
  430.     msmsg("If the GAME disk is not in, put it in now.\n");
  431.     getreturn("to continue");
  432.     }
  433. }
  434.  
  435. /* Read configuration */
  436. void
  437. read_config_file()
  438. {
  439.     char    tmp_ramdisk[PATHLEN], tmp_levels[PATHLEN];
  440.     char    buf[BUFSZ], *bufp;
  441.     FILE    *fp, *fopenp();
  442.     extern  char plname[];
  443.     extern  int saveprompt;
  444.  
  445.     tmp_ramdisk[0] = 0;
  446.     tmp_levels[0] = 0;
  447.     if ((fp = fopenp(configfile, "r")) == NULL) {
  448.     msmsg("Warning: no configuration file!\n");
  449.     getreturn("to continue");
  450.     return;
  451.     }
  452.     while (fgets(buf, BUFSZ, fp)) {
  453.     if (*buf == '#')
  454.         continue;
  455.  
  456.     /* remove trailing whitespace */
  457.  
  458.     bufp = index(buf, '\n');
  459.     while (bufp > buf && isspace(*bufp))
  460.         bufp--;
  461.     if (bufp == buf)
  462.         continue;         /* skip all-blank lines */
  463.     else
  464.         *(bufp + 1) = 0;    /* 0 terminate line */
  465.  
  466.     /* find the '=' */
  467.     if (!(bufp = index(buf, '='))) {
  468.         msmsg("Bad option line: '%s'\n", buf);
  469.         getreturn("to continue");
  470.         continue;
  471.     }
  472.  
  473.     /* skip  whitespace between '=' and value */
  474.     while (isspace(*++bufp))
  475.         ;
  476.  
  477.     /* Go through possible variables */
  478.     if (!strncmp(buf, "HACKDIR", 4)) {
  479.         strncpy(hackdir, bufp, PATHLEN);
  480.  
  481.     } else if (!strncmp(buf, "RAMDISK", 3)) {
  482.         strncpy(tmp_ramdisk, bufp, PATHLEN);
  483.  
  484.     } else if (!strncmp(buf, "LEVELS", 4)) {
  485.         strncpy(tmp_levels, bufp, PATHLEN);
  486.  
  487.     } else if (!strncmp(buf, "OPTIONS", 4)) {
  488.         parseoptions(bufp, TRUE);
  489.         if (plname[0])        /* If a name was given */
  490.         plnamesuffix();    /* set the character class */
  491.  
  492.     } else if (!strncmp(buf, "SAVE", 4)) {
  493.         char *ptr;
  494.         if (ptr = index(bufp, ';')) {
  495.         *ptr = '\0';
  496.         if (*(ptr+1) == 'n' || *(ptr+1) == 'N')
  497.             saveprompt = FALSE;
  498.         }
  499.         (void) strncpy(SAVEF, bufp, PATHLEN);
  500.         append_slash(SAVEF);
  501. #ifdef GRAPHICS
  502.     } else if (!strncmp(buf, "GRAPHICS", 4)) {
  503.         unsigned int translate[MAXPCHARS];
  504.         int  i;
  505.  
  506.         if ((i = sscanf(bufp, "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d",
  507.         &translate[ 0], &translate[ 1], &translate[ 2],
  508.         &translate[ 3], &translate[ 4], &translate[ 5],
  509.         &translate[ 6], &translate[ 7], &translate[ 8],
  510.         &translate[ 9], &translate[10], &translate[11],
  511.         &translate[12], &translate[13], &translate[14],
  512.         &translate[15], &translate[16], &translate[17])) < 0) {
  513.             msmsg ("Syntax error in GRAPHICS\n");
  514.             getreturn("to continue");
  515.         } /* Yuck! Worked only with low-byte first!!! */
  516. /*
  517.  * You could have problems here if you configure FOUNTAINS, SPIDERS or NEWCLASS
  518.  * in or out and forget to change the tail entries in your graphics string.
  519.  */
  520. #define SETPCHAR(f, n)    showsyms.f = (i > n) ? translate[n] : defsyms.f
  521.         SETPCHAR(stone, 0);
  522.         SETPCHAR(vwall, 1);
  523.         SETPCHAR(hwall, 2);
  524.         SETPCHAR(tlcorn, 3);
  525.         SETPCHAR(trcorn, 4);
  526.         SETPCHAR(blcorn, 5);
  527.         SETPCHAR(brcorn, 6);
  528.         SETPCHAR(door, 7);
  529.         SETPCHAR(room, 8);
  530.         SETPCHAR(corr, 9);
  531.         SETPCHAR(upstair, 10);
  532.         SETPCHAR(dnstair, 11);
  533.         SETPCHAR(trap, 12);
  534. #ifdef FOUNTAINS
  535.         SETPCHAR(pool, 13);
  536.         SETPCHAR(fountain, 14);
  537. #endif
  538. #ifdef NEWCLASS
  539.         SETPCHAR(throne, 15);
  540. #endif
  541. #ifdef SPIDERS
  542.         SETPCHAR(web, 16);
  543. #endif
  544. #ifdef SINKS
  545.         SETPCHAR(sink, 17);
  546. #endif
  547. #undef SETPCHAR
  548. #endif /* GRAPHICS */
  549.     } else if (!strncmp(buf, "PATH", 4)) {
  550.         strncpy(PATH, bufp, PATHLEN);
  551.  
  552.     } else {
  553.         msmsg("Bad option line: '%s'\n", buf);
  554.         getreturn("to continue");
  555.     }
  556.     }
  557.     fclose(fp);
  558.  
  559.     strcpy(permbones, tmp_levels);
  560.     if (tmp_ramdisk[0]) {
  561.     strcpy(levels, tmp_ramdisk);
  562.     if (strcmpi(permbones, levels))        /* if not identical */
  563.         ramdisk = TRUE;
  564.     } else
  565.     strcpy(levels, tmp_levels);
  566.     strcpy(bones, levels);
  567. }
  568.  
  569. /* Set names for bones[] and lock[] */
  570.  
  571. void
  572. set_lock_and_bones()
  573. {
  574.     if (!ramdisk) {
  575.     strcpy(levels, permbones);
  576.     strcpy(bones, permbones);
  577.     }
  578.     append_slash(permbones);
  579.     append_slash(levels);
  580.     append_slash(bones);
  581.     strcat(bones, allbones);
  582.     strcpy(lock, levels);
  583.     strcat(lock, alllevels);
  584. }
  585.  
  586. /*
  587.  * Add a slash to any name not ending in / or :.  There must
  588.  * be room for the /.
  589.  */
  590. void
  591. append_slash(name)
  592. char *name;
  593. {
  594.     char *ptr;
  595.  
  596.     if (!*name)
  597.     return;
  598.     ptr = name + (strlen(name) - 1);
  599.     if (*ptr != '/' && *ptr != ':') {
  600.     *++ptr = '/';
  601.     *++ptr = '\0';
  602.     }
  603. }
  604.  
  605.  
  606. void
  607. getreturn(str)
  608. char *str;
  609. {
  610.     int ch;
  611.  
  612.     msmsg("Hit <RETURN> %s.", str);
  613.     while ((ch = getchar()) != '\n')
  614.     ;
  615. }
  616.  
  617. void
  618. msmsg(fmt, a1, a2, a3)
  619. char *fmt;
  620. long a1, a2, a3;
  621. {
  622.     printf(fmt, a1, a2, a3);
  623.     (void) fflush(stdout);
  624. }
  625.  
  626. /* Follow the PATH, trying to fopen the file.
  627.  */
  628. #define PATHSEP    ';'
  629. #undef fopen
  630.  
  631. FILE *
  632. fopenp(name, mode)
  633. register char *name, *mode;
  634. {
  635.     char buf[BUFSIZ], *bp, *pp, lastch;
  636.     FILE *fp;
  637.     register struct FileLock *theLock;
  638.  
  639.     /* Try the default directory first.  Then look along PATH.
  640.      */
  641.     strcpy(buf, name);
  642.     if (theLock = Lock(buf, SHARED_LOCK)) {
  643.     UnLock(theLock);
  644.     if (fp = fopen(buf, mode))
  645.         return fp;
  646.     }
  647.     pp = PATH;
  648.     while (pp && *pp) {
  649.     bp = buf;
  650.     while (*pp && *pp != PATHSEP)
  651.         lastch = *bp++ = *pp++;
  652.     if (lastch != ':' && lastch != '/' && bp != buf)
  653.         *bp++ = '/';
  654.     strcpy(bp, name);
  655.     if (theLock = Lock(buf, SHARED_LOCK)) {
  656.         UnLock(theLock);
  657.         if (fp = fopen(buf, mode))
  658.         return fp;
  659.     }
  660.     if (*pp)
  661.         pp++;
  662.     }
  663.     return NULL;
  664. }
  665. #endif /* DGK */
  666.  
  667. #ifdef CHDIR
  668.  
  669. /*
  670.  *  A not general-purpose directory changing routine.
  671.  *  Assumes you want to return to the original directory eventually,
  672.  *  by chdir()ing to orgdir.
  673.  *  Assumes -1 is not a valid lock, since 0 is valid.
  674.  */
  675.  
  676. #define NO_LOCK     ((struct FileLock *) -1)
  677.  
  678. char orgdir[1];
  679. static struct FileLock *OrgDirLock = NO_LOCK;
  680.  
  681. chdir(dir)
  682. char *dir;
  683. {
  684.     if (dir == orgdir) {
  685.     /* We want to go back to where we came from. */
  686.     if (OrgDirLock != NO_LOCK) {
  687.         UnLock(CurrentDir(OrgDirLock));
  688.         OrgDirLock = NO_LOCK;
  689.     }
  690.     } else {
  691.     /*
  692.      * Go to some new place. If still at the original
  693.      * directory, save the FileLock.
  694.      */
  695.     struct FileLock *newDir;
  696.  
  697.     if (newDir = Lock(dir, SHARED_LOCK)) {
  698.         if (OrgDirLock == NO_LOCK) {
  699.         OrgDirLock = CurrentDir(newDir);
  700.         } else {
  701.         UnLock(CurrentDir(newDir));
  702.         }
  703.     } else {
  704.         return -1;    /* Failed */
  705.     }
  706.     }
  707.     /* CurrentDir always succeeds if you have a lock */
  708.     return 0;
  709. }
  710.  
  711. #endif
  712.  
  713. /* Chdir back to original directory
  714.  */
  715. #undef exit
  716. void
  717. msexit(code)
  718. {
  719. #ifdef CHDIR
  720.     extern char orgdir[];
  721. #endif
  722.  
  723. #ifdef DGK
  724.     (void) fflush(stdout);
  725.     if (ramdisk)
  726.     copybones(TOPERM);
  727. #endif
  728. #ifdef CHDIR
  729.     chdir(orgdir);      /* chdir, not chdirx */
  730. #endif
  731.     /* Never know when exit is called... */
  732.     if (Initialized && (curx != 1 || cury != 1))
  733.     getret();
  734.  
  735.     CleanUp();
  736.     exit(code);
  737. }
  738.  
  739. /*
  740.  *  Strcmp while ignoring case. Not general-purpose, so static.
  741.  */
  742.  
  743. static int strcmpi(a, b)
  744. register char *a, *b;
  745. {
  746.     while (tolower(*a) == tolower(*b)) {
  747.     if (!*a)        /* *a == *b, so at end of both strings */
  748.         return 0;    /* equal. */
  749.     a++;
  750.     b++;
  751.     }
  752.     return 1;
  753. }
  754.  
  755. /*
  756.  *  cmpmem - used to compare two struct symbols, in lev.c
  757.  */
  758.  
  759. cmpmem(a, b, size)
  760. register unsigned char *a, *b;
  761. register int size;
  762. {
  763.     while (size--) {
  764.     if (*a++ != *b++)
  765.         return 1;    /* not equal */
  766.     }
  767.  
  768.     return 0;        /* equal */
  769. }
  770.